#include <math.h>
#include <stdlib.h>

#include "vector"

#include "glowerbound.h"
#include "gbatchInformation.h"

//Uncomment to debug lowerbound
#define DEBUG_LOWERBOUND





/** -----------------------------------------------------------------------------------------
 * checks if the solution satisfies the constraints
 *
 * @return true if the constraints are satisfied
 */
GLowerBound::GLowerBound(GSupplyLinkProblem* pb) {
  problem = pb ;
  np = problem->getNP() ;

}



/** ###################################################################################
 *
 *                              GLOWERBOUND V0
 * 
 *  ###################################################################################
 */


/** -----------------------------------------------------------------------------------------
 * evaluates the bin packing L1 lowerbound for the jobs 1..jobStart-1
 *
 * @return L1
 */
int GLowerBoundV0::evaluateNewNeededBins(GSupplyLinkSolution& sol, int jobStart) {
  double sum = 0.0 ;
  double residualCapacity = 0.0 ;
  
  for (int b=sol.nbrBatch-1;b>=0;--b) {
    residualCapacity += ( problem->getTransporter(0).getCapacity() - sol.tabBatchInformation[b].size ) ;
  }

  for (int i=0;i<jobStart;i++) {
    sum+= problem->getJobSize(i) ;
  }
  if (residualCapacity<0) residualCapacity = 0 ;

  double sumreduced = sum - residualCapacity ;
  if (sumreduced<0) 
    sumreduced = 0.0 ;

  double L1 = sumreduced/problem->getTransporter(0).getCapacity() ;
  int    ceilL1 = (int)ceil(L1) ;

#ifdef DEBUG_LOWERBOUND
  cout << "      jobStart........................="<< jobStart <<endl ;
  cout << "      residualCapacity................="<< residualCapacity<<endl ;
  cout << "      sumweight.......................=" << sum <<endl ;
  cout << "      L1.V0...........................=" << ceilL1 << endl ;
#endif

  return ceilL1 ;
}



/** -----------------------------------------------------------------------------------------
 * evaluates the solution
 *
 * @return evaluation
 */
void GLowerBoundV0::evaluate(GSupplyLinkSolution& sol, int jobStart) {
  //  GSupplyLinkSolution solsave (sol) ;

 if (!sol.checkConstraints(jobStart, np-1)) {
    nbrBatchLB = -1 ;
    customerHoldingCostLB = -1.0 ;
    return ;
  }

  // Nbr Batch minimum = nbr batch already used + lowerbound L1 of remaining jobs
  int newNeededBins = evaluateNewNeededBins(sol, jobStart) ;
  nbrBatchLB = sol.getNbrBatch() + newNeededBins ;

#ifdef DEBUG_LOWERBOUND
cout << "    nbrBatch=" << sol.getNbrBatch() << " and newNeededBins=" << newNeededBins << " => nbrBatchLB=" << nbrBatchLB <<endl ;
#endif

  // Customer Holding Cost
  customerHoldingCostLB = 0 ;
  for (int i=jobStart;i<problem->getNP();++i) {
    double jobcustcost = problem->getJobHoldingCost(i) ;
    double jobduedate = problem->getJobDueDate(i) ;
    double jobcomptime =  sol.processingSchedule[i].getDeliveryCompletionTime() ;
    customerHoldingCostLB += jobcustcost*(jobduedate-jobcomptime) ;
  }

#ifdef DEBUG_LOWERBOUND
cout << "    customerHoldingCostLB=" << customerHoldingCostLB << endl ;
#endif
}


/** ###################################################################################
 *
 *                              GLOWERBOUND V0
 * 
 *  ###################################################################################
 */


/** -----------------------------------------------------------------------------------------
 * evaluates the bin packing L1 lowerbound for the jobs 1..jobStart-1
 *
 * @return L1
 */
int GLowerBoundV1::evaluateNewNeededBins(GSupplyLinkSolution& sol, int jobStart) {
  double sumCannotBeReduced = 0.0 ;
  double sumCanBeReduced = 0.0 ;
  double residualCapacity = 0.0 ;
  
  for (int b=sol.nbrBatch-1;b>=0;--b) {
    residualCapacity += ( problem->getTransporter(0).getCapacity() - sol.tabBatchInformation[b].size ) ;
  }

  for (int i=0;i<jobStart;i++) {
    int b=0 ;
    bool fit=false ;
    while (b<sol.nbrBatch-1 && !fit) {
      if (problem->getJobSize(i) >= problem->getTransporter(0).getCapacity() - sol.tabBatchInformation[b].size)
    	fit = true ;
      b++ ;
    }
    if (fit) 
      sumCanBeReduced += problem->getJobSize(i) ;
    else
      sumCannotBeReduced += problem->getJobSize(i) ;
  }

#ifdef DEBUG_LOWERBOUND
  cout << "      jobStart........................="<< jobStart <<endl ;
  cout << "      residualCapacity................="<< residualCapacity<<endl ;
  cout << "      sumCannotBeReduced =" << sumCannotBeReduced <<endl ;
  cout << "      sumCanBeReduced    =" << sumCanBeReduced <<endl ;
#endif

  sumCanBeReduced -= residualCapacity ;
  if (sumCanBeReduced<0) 
    sumCanBeReduced = 0.0 ;

  double L1 = (sumCanBeReduced+sumCannotBeReduced)/problem->getTransporter(0).getCapacity() ;
  int    ceilL1 = (int)ceil(L1) ;

#ifdef DEBUG_LOWERBOUND
  cout << "      sums remaining for new bins.....=" << (sumCanBeReduced+sumCannotBeReduced) << endl ;
  cout << "      L1 V1...........................=" << ceilL1 << endl ;
#endif

  return ceilL1 ;
}




/** -----------------------------------------------------------------------------------------
 * evaluates the solution
 *
 * @return evaluation
 */
void GLowerBoundV1::evaluate(GSupplyLinkSolution& sol, int jobStart) {
  //  GSupplyLinkSolution solsave (sol) ;

 if (!sol.checkConstraints(jobStart, np-1)) {
    nbrBatchLB = -1 ;
    customerHoldingCostLB = -1.0 ;
    return ;
  }

  // Nbr Batch minimum = nbr batch already used + lowerbound L1 of remaining jobs
  int newNeededBins = evaluateNewNeededBins(sol, jobStart) ;
  nbrBatchLB = sol.getNbrBatch() + newNeededBins ;

#ifdef DEBUG_LOWERBOUND
cout << "    nbrBatch=" << sol.getNbrBatch() << " and newNeededBins=" << newNeededBins << " => nbrBatchLB=" << nbrBatchLB <<endl ;
#endif

  // Customer Holding Cost
  customerHoldingCostLB = 0 ;
  for (int i=jobStart;i<problem->getNP();++i) {
    double jobcustcost = problem->getJobHoldingCost(i) ;
    double jobduedate = problem->getJobDueDate(i) ;
    double jobcomptime =  sol.processingSchedule[i].getDeliveryCompletionTime() ;
    customerHoldingCostLB += jobcustcost*(jobduedate-jobcomptime) ;
  }

#ifdef DEBUG_LOWERBOUND
cout << "    customerHoldingCostLB=" << customerHoldingCostLB << endl ;
#endif
}


